from TokenType import TokenType
from decimal import Decimal


class Ast:

    def __repr__(self):
        return self.__str__()

    def pprint(self):
        pass

    def get_value(self, variable) -> Decimal:
        pass


class BinOpAst(Ast):
    left = None
    token = None
    right = None

    def __init__(self, left, token, right):
        self.left = left
        self.token = token
        self.right = right

    def __str__(self):
        return f'BinOpAst({repr(self.left)}, {repr(self.token)}, {repr(self.right)})'

    def pprint(self):
        return f'({self.left.pprint()} {self.token.pprint()} {self.right.pprint()})'

    def get_value(self, variable) -> Decimal:
        left_value = self.left.get_value(variable)
        right_value = self.right.get_value(variable)
        if left_value is None or right_value is None:
            raise Exception('二元运算的左或右是空的')
        if self.token.type == TokenType.PLUS:
            return left_value + right_value
        if self.token.type == TokenType.MINUS:
            return left_value - right_value
        if self.token.type == TokenType.MULTIPLY:
            return left_value * right_value
        if self.token.type == TokenType.DIVIDE:
            return left_value / right_value
        if self.token.type == TokenType.POWER:
            return left_value ** right_value


class NumAst(Ast):
    value: Decimal = None

    def __init__(self, token):
        self.value = token.value

    def __str__(self):
        return f'NumAst({self.value})'

    def pprint(self):
        return self.value

    def get_value(self, variable) -> Decimal:
        return self.value


class NegativeAst(Ast):
    ast: Ast = None

    def __init__(self, ast: Ast):
        self.ast = ast

    def __str__(self):
        return f'NegativeAst({repr(self.ast)})'

    def pprint(self):
        return f'-{self.ast.pprint()}'

    def get_value(self, variable) -> Decimal:
        return -self.ast.get_value(variable)


class VariableAst(Ast):
    key = None

    def __init__(self, token):
        self.key = token.value

    def __str__(self):
        return f'VariableAst({repr(self.key)})'

    def pprint(self):
        return self.key

    def get_value(self, variable) -> Decimal:
        return Decimal(variable.get(self.key))
